//*******1*********2*********3*********4*********5*********6*********7*********8
/* 
 Tree234Tester.java
 Daniel Johnson
 johnda09@metrostate.edu
 */

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Point;

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.JButton;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class Tree234Tester {

	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable() {
			public void run()
			{
				try
				{
					Tree234Tester window = new Tree234Tester();
					window.appFrame.setVisible(true);
				} catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public Tree234Tester()
	{
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize()
	{
		JPanel tree_order_panel = new JPanel();
		JPanel control_panel = new JPanel();
		FlowLayout flowLayout;

		JLabel node_input_label;
		JButton insert_button;
		JButton find_button;
		JButton delete_button;
		JButton empty_button;
		JButton test_button;
		// JButton in_order_btn;
		// JButton pre_order_btn;
		// JButton post_order_btn;

		appFrame = new JFrame();
		appFrame.setBounds(100, 100, FRAME_WIDTH, FRAME_HEIGHT);
		appFrame.setTitle(APP_TITLE);
		appFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Section to setup area for displaying results of Tree traversal
		flowLayout = (FlowLayout) tree_order_panel.getLayout();
		flowLayout.setAlignment(FlowLayout.LEFT);
		flowLayout.setVgap(10);

		// in_order_btn = new JButton("In-Order");
		// in_order_btn.addActionListener(new InOrderTraversal());
		// pre_order_btn = new JButton("Pre-Order");
		// pre_order_btn.addActionListener(new PreOrderTraversal());
		// post_order_btn = new JButton("Post-Order");
		// post_order_btn.addActionListener(new PostOrderTraversal());
		outputLabel = new JLabel();
		outputText = new JLabel();

		// tree_order_panel.add(in_order_btn);
		// tree_order_panel.add(pre_order_btn);
		// tree_order_panel.add(post_order_btn);
		tree_order_panel.add(outputLabel);
		tree_order_panel.add(outputText);
		appFrame.getContentPane().add(tree_order_panel, BorderLayout.NORTH);

		// Section to setup controls for Tree interaction
		node_input_label = new JLabel("Input Tree Node(s)");
		node_input_label.setHorizontalAlignment(SwingConstants.RIGHT);

		nodeDataInput = new JTextField();
		nodeDataInput
				.setToolTipText("Enter list of integers, delimited by any non-numeric character");
		nodeDataInput.setColumns(10);

		insert_button = new JButton("Insert");
		insert_button.addActionListener(new InsertAction());

		find_button = new JButton("Find");
		find_button.addActionListener(new FindAction());

		delete_button = new JButton("Delete");
		delete_button.addActionListener(new DeleteAction());

		empty_button = new JButton("Empty");
		empty_button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e)
			{
				if (currentTree != null)
				{
					currentTree.clear();
					// Re-draw tree with any changes
					updateTreeGraph();
				}
			}
		});

		test_button = new JButton("Test");
		test_button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e)
			{
				// TODO Temporary button for mass list (testing)
				Integer int_count, test_int;
				ArrayList<Integer> int_list = new ArrayList<Integer>();

				try
				{
					int_count = Integer.parseInt(nodeDataInput.getText());
					// Build list of integers
					for (int i = 0; i < int_count; i++)
					{
						// Generate a random int, and test for it in list
						do
						{
							test_int = (int) (ELEMENT_MAX * Math.random());
						} while (int_list.contains(test_int));

						int_list.add(test_int);
					}
					nodeDataInput.setText(int_list.toString());
				} catch (Exception ex)
				{
					// Trap any errors
					System.out.println("Test Error");
				}
			}
		});

		flowLayout = (FlowLayout) control_panel.getLayout();
		flowLayout.setVgap(10);
		flowLayout.setHgap(10);
		control_panel.add(node_input_label);
		control_panel.add(nodeDataInput);
		control_panel.add(insert_button);
		control_panel.add(find_button);
		control_panel.add(delete_button);
		control_panel.add(empty_button);
		control_panel.add(test_button);
		appFrame.getContentPane().add(control_panel, BorderLayout.SOUTH);

		// Section to setup area for drawing tree (add as last in container)
		graphPanel = null;
	}

	private Integer[] parseNodeData()
	{
		ArrayList<Integer> node_ints;
		String current_int = "";
		StringBuffer text_input;

		if (!nodeDataInput.getText().isEmpty())
		{
			node_ints = new ArrayList<Integer>();
			text_input = new StringBuffer(nodeDataInput.getText());

			while (text_input.length() > 0)
			{
				// Construct integer from numeric chars
				if (Character.isDigit(text_input.charAt(0)))
				{
					// Begin new integer string
					if (current_int == "")
					{
						current_int = text_input.substring(0, 1);
					}
					// Append to existing integer string
					else
					{
						current_int += text_input.substring(0, 1);
					}
				}
				// Store constructed integer when non-numeric char is found
				else
					if (current_int != "")
					{
						node_ints.add(Integer.parseInt(current_int));
						current_int = "";
					}

				// Remove processed (leading) character
				text_input.deleteCharAt(0);
			}

			// Store last constructed integer (if any), when loop terminates
			if (current_int != "")
			{
				node_ints.add(Integer.parseInt(current_int));
				current_int = "";
			}

			// Clear last input
			nodeDataInput.setText(null);

			// Check if no integers were parsed
			if (node_ints.isEmpty())
			{
				return null;
			}

			// Convert ArrayList to array
			return node_ints.toArray(new Integer[node_ints.size()]);
		}

		return null;
	}

	/**
	 * Private method to initialize the tree on demand.
	 * 
	 * @return the current tree
	 */
	private Tree234<Integer> getCurrentTree()
	{
		if (currentTree == null)
		{
			this.currentTree = new Tree234<Integer>();
		}

		return currentTree;
	}

	private void updateTreeGraph()
	{
		if (graphPanel != null)
		{
			// Remove existing drawing panel
			appFrame.getContentPane().remove(graphPanel);
			graphPanel = null;
		}

		// Clear previous informational output
		clearOutput();
		// Construct new graph panel, reading the current Tree
		graphPanel = new Tree234GraphPanel<Integer>(
				getCurrentTree(),
				new Point(FRAME_WIDTH / 2, Tree234GraphPanel.TREE_BORDER_OFFSET));
		appFrame.getContentPane().add(graphPanel, BorderLayout.CENTER);
		// Necessary to update contents after removal/addition of components
		appFrame.validate();
	}

	// private void printInOrder() {
	// final String ORDER_LABEL = "In-Order Traversal:";
	// StringBuffer print_out;
	//
	// if (getCurrentTree().getRoot() != null) {
	// print_out = new StringBuffer();
	// getCurrentTree().getRoot().printInOrder(print_out);
	// printOutput(ORDER_LABEL, print_out.toString());
	// }
	// else {
	// printOutput(ORDER_LABEL, "");
	// }
	// }

	// private void printPreOrder() {
	// final String ORDER_LABEL = "Pre-Order Traversal:";
	// StringBuffer print_out;
	//
	// if (getCurrentTree().getRoot() != null) {
	// print_out = new StringBuffer();
	// getCurrentTree().getRoot().printPreOrder(print_out);
	// printOutput(ORDER_LABEL, print_out.toString());
	// }
	// else {
	// printOutput(ORDER_LABEL, "");
	// }
	// }

	// private void printPostOrder() {
	// final String ORDER_LABEL = "Post-Order Traversal:";
	// StringBuffer print_out;
	//
	// if (getCurrentTree().getRoot() != null) {
	// print_out = new StringBuffer();
	// getCurrentTree().getRoot().printPostOrder(print_out);
	// printOutput(ORDER_LABEL, print_out.toString());
	// }
	// else {
	// printOutput(ORDER_LABEL, "");
	// }
	// }

	private void printOutput(String label, String output)
	{
		outputLabel.setText(label);
		outputText.setText(output);
	}

	private void clearOutput()
	{
		outputLabel.setText("");
		outputText.setText("");
	}

	// /**
	// * Handles response to user clicking "In-Order" button. Displays an
	// * in-order traversal of a Tree.
	// *
	// * @author johnda09@metrostate.edu
	// *
	// */
	// private class InOrderTraversal implements ActionListener {
	// @Override
	// public void actionPerformed(ActionEvent e) {
	// printInOrder();
	// }
	// }

	// /**
	// * Handles response to user clicking "Pre-Order" button. Displays
	// * a pre-order traversal of a Tree.
	// *
	// * @author johnda09@metrostate.edu
	// *
	// */
	// private class PreOrderTraversal implements ActionListener {
	// @Override
	// public void actionPerformed(ActionEvent e) {
	// printPreOrder();
	// }
	// }

	// /**
	// * Handles response to user clicking "Post-Order" button. Displays
	// * a post-order traversal of a Tree.
	// *
	// * @author johnda09@metrostate.edu
	// *
	// */
	// private class PostOrderTraversal implements ActionListener {
	// @Override
	// public void actionPerformed(ActionEvent e) {
	// printPostOrder();
	// }
	// }

	/**
	 * Handles response to user clicking "Insert" button. Inserts a node into
	 * the Tree
	 * 
	 * @author johnda09@metrostate.edu
	 * 
	 */
	private class InsertAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e)
		{

			// Array of Integers to be inserted
			Integer[] node_els = parseNodeData();

			// Ignore empty input
			if (node_els != null)
			{
				// Insert nodes for each item in array
				for (int i = 0; i < node_els.length; i++)
				{

					try
					{
						getCurrentTree().insert(node_els[i]);
					}
					// Insert method throws DuplicateItemException
					catch (Exception ex)
					{
					}
				}

				// Re-draw tree with any changes
				updateTreeGraph();
				System.out.print(" After insert: ");
				System.out.println(getCurrentTree().inOrderTraversal());
			}
		}
	}

	/**
	 * Handles response to user clicking "Find" button. Finds a node into the
	 * Tree
	 * 
	 * @author johnda09@metrostate.edu
	 * 
	 */
	private class FindAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e)
		{

			// Array of Integers to be found
			Integer[] node_els = parseNodeData();
			// Node234<Integer> found;
			//
			// // Ignore empty input
			// if (node_data != null) {
			// // Find nodes for each item in array
			// for (int i = 0; i < node_data.length; i++) {
			//
			// // Find each node in item array
			// found = getCurrentTree().findNode(node_data[i]);
			//
			// if (found != null) {
			// found.colorNode(graphPanel.getGraphics(), Color.GREEN);
			// }
			// }
			// }

			boolean found = false;
			StringBuilder found_els = new StringBuilder("Found { ");
			StringBuilder missing_els = new StringBuilder("Not Found { ");

			// Ignore empty input
			if (node_els != null)
			{
				// Find nodes for each item in array
				for (int i = 0; i < node_els.length; i++)
				{

					// Find each node in item array
					found = getCurrentTree().search(node_els[i]);

					// TODO: use updating of Tree Graphics;
					if (found)
					{
						found_els.append(node_els[i] + " ");
					} else
					{
						missing_els.append(node_els[i] + " ");
					}
				}

				found_els.append("}");
				missing_els.append("}");

				printOutput("Element Search:", found_els.toString() + ", "
						+ missing_els.toString());
			}
		}
	}

	/**
	 * Handles response to user clicking "Delete" button. Deletes nodes from the
	 * Tree, according to a given list.
	 * 
	 * @author johnda09@metrostate.edu
	 * 
	 */
	private class DeleteAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e)
		{

			// Array of Integers to be deleted
			Integer[] node_els = parseNodeData();

			// Ignore empty input
			if (node_els != null)
			{
				// Delete any node matching an item in the array
				for (int i = 0; i < node_els.length; i++)
				{

					try
					{
						getCurrentTree().delete(node_els[i]);
					}
					// Delete method throws ItemNotFoundException
					catch (ItemNotFoundException ex)
					{
					}
				}

				System.out.print("Before delete: ");
				System.out.println(getCurrentTree().inOrderTraversal());
				// Re-draw tree with any changes
				updateTreeGraph();
				System.out.print(" After delete: ");
				System.out.println(getCurrentTree().inOrderTraversal());
			}
		}
	}

	// State tree containing all current nodes
	private Tree234<Integer> currentTree;

	// Application window
	private JFrame appFrame;
	// Panel for drawing tree
	private Tree234GraphPanel<Integer> graphPanel;
	// Text input for node data
	private JTextField nodeDataInput;
	// Label to display type of traversal processed
	private JLabel outputLabel;
	// Area to display node data from Tree traversal
	private JLabel outputText;

	// Initial window width
	public static final int FRAME_WIDTH = 800;
	// Initial window height
	public static final int FRAME_HEIGHT = 600;
	private static final int ELEMENT_MAX = 100;
	// Titlebar string
	private static final String APP_TITLE = "TESTING - Tree234 Builder";
}
